package org.loong.crypto.service.core.provider.impl;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.crypto.SecretKey;

import org.loong.crypto.common.exception.InvalidKeyException;
import org.loong.crypto.core.algorithm.CipherAlgorithm;
import org.loong.crypto.core.algorithm.EncryptionMethod;

/**
 * The base abstract class for AES encrypter and decrypter.
 * 
 * <p>
 * Supports the following algorithms:
 *
 * <ul>
 *     <li>{@link org.loong.crypto.core.algorithm.CipherAlgorithm#AES}
 * </ul>
 * 
 * <p>
 * Supports the following encrypt methods:
 *
 * <ul>
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES128_ECB}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES192_ECB}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES256_ECB}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES128_CBC}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES192_CBC}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES256_CBC}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES128_CFB}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES192_CFB}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES256_CFB}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES128_OFB}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES192_OFB}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES256_OFB}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES128_ECB_PKCS5}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES192_ECB_PKCS5}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES256_ECB_PKCS5}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES128_CBC_PKCS5}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES192_CBC_PKCS5}
 *     <li>{@link org.loong.crypto.core.algorithm.EncryptionMethod#AES256_CBC_PKCS5}
 * </ul>
 */
public abstract class AESCipherProvider extends BaseCipherProvider {

    /**
     * The supported encryption algorithms by the AES cipher provider class.
     */
    public static final Set<CipherAlgorithm> SUPPORTED_ALGORITHMS;

    /**
     * The supported encryption methods by the AES cipher provider class.
     */
    public static final Set<EncryptionMethod> SUPPORTED_ENCRYPTION_METHODS;

    static {
        Set<CipherAlgorithm> algs = new LinkedHashSet<>();
        algs.add(CipherAlgorithm.AES);
        SUPPORTED_ALGORITHMS = Collections.unmodifiableSet(algs);

        Set<EncryptionMethod> encs = new LinkedHashSet<>();
        encs.add(EncryptionMethod.AES128_ECB);
        encs.add(EncryptionMethod.AES192_ECB);
        encs.add(EncryptionMethod.AES256_ECB);
        
        encs.add(EncryptionMethod.AES128_CBC);
        encs.add(EncryptionMethod.AES192_CBC);
        encs.add(EncryptionMethod.AES256_CBC);
        
        encs.add(EncryptionMethod.AES128_CFB);
        encs.add(EncryptionMethod.AES192_CFB);
        encs.add(EncryptionMethod.AES256_CFB);
        
        encs.add(EncryptionMethod.AES128_OFB);
        encs.add(EncryptionMethod.AES192_OFB);
        encs.add(EncryptionMethod.AES256_OFB);
        
        encs.add(EncryptionMethod.AES128_ECB_PKCS5);
        encs.add(EncryptionMethod.AES192_ECB_PKCS5);
        encs.add(EncryptionMethod.AES256_ECB_PKCS5);
        
        encs.add(EncryptionMethod.AES128_CBC_PKCS5);
        encs.add(EncryptionMethod.AES192_CBC_PKCS5);
        encs.add(EncryptionMethod.AES256_CBC_PKCS5);
        SUPPORTED_ENCRYPTION_METHODS = Collections.unmodifiableSet(encs);
    }

    /**
     * The Encryption Key.
     */
    private final SecretKey secretKey;

    /**
     * Creates a new AES provider.
     * 
     * @param secretKey the secretKey
     */
    protected AESCipherProvider(final SecretKey secretKey) {
        super(SUPPORTED_ALGORITHMS, SUPPORTED_ENCRYPTION_METHODS);

        if (secretKey == null) {
            throw new InvalidKeyException("The Secret key must not be null");
        }
        
        this.secretKey = secretKey;
    }

    /**
     * Gets the Key Encryption Key.
     *
     * @return the Key Encryption Key.
     */
    public SecretKey getSecretKey() {
        return secretKey;
    }
}
